package com.karson.service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.karson.common.enums.FileCategory;
import com.karson.common.model.Subtitles;
import com.karson.common.model.TrackInfo;
import com.karson.common.model.TrackRate;
import com.karson.common.request.FileRequest;
import com.karson.service.service.ISimilarityService;
import com.karson.service.service.SubtitlesService;
import com.karson.service.service.TrackInfoService;
import com.karson.service.service.TrackRateService;
import org.apache.commons.text.similarity.CosineSimilarity;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.ja.JapaneseAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.karson.common.enums.FileCategory.detect;

@Service
public class ISimilarityServiceImpl implements ISimilarityService {
    @Autowired
    private TrackInfoService trackInfoService;
    @Autowired
    private TrackRateService trackRateService;
    @Autowired
    private SubtitlesService subtitlesService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean uploadExplain(Integer albumId, List<FileRequest> files) throws Exception {
        // 通过专辑id查找歌曲
        LambdaQueryWrapper<TrackInfo> trackQueryWrapper = new LambdaQueryWrapper<>();
        trackQueryWrapper.eq(TrackInfo::getAlbumId, albumId);
        List<TrackInfo> trackInfoList = trackInfoService.list(trackQueryWrapper);
        Map<TrackInfo, List<FileRequest>> trackMap = trackInfoList.stream()
                .collect(Collectors.toMap(
                        trackInfo -> trackInfo,
                        trackInfo -> new ArrayList<>(),
                        (list1, list2) -> list1  // 如果歌曲名重复，保留第一个
                ));
        Map<TrackInfo, List<FileRequest>> trackInfoListMap = aggregateMultiRound(trackMap, files, 0.85, 0.5, 0.2);
        List<TrackRate> trackRates = new ArrayList<>();
        List<Subtitles> subTitleList = new ArrayList<>();
        trackInfoListMap.forEach((track, fileList) -> {
            fileList.forEach(file -> {
                FileCategory category = detect(file);
                switch (category) {
                    case AUDIO:
                        System.out.println("音频文件: " + file.getFileName());
                        TrackRate trackRate = new TrackRate();
                        trackRate.setTrackId(track.getId());
                        trackRate.setMediaFileName(file.getFileName());
                        trackRate.setMediaUrl(file.getFileUrl());
                        trackRate.setMediaSize(file.getFileSize());
                        trackRate.setMediaType(file.getFileType());
                        trackRate.setMediaFileId(file.getFileId());
                        System.out.println(track.getTrackTitle() + "音频文件: " + trackRate);
                        trackRates.add(trackRate);
                        break;
                    case SUBTITLE:
                        Subtitles subtitles = new Subtitles();
                        subtitles.setTrackId(track.getId());
                        subtitles.setAlbumId(albumId);
                        subtitles.setFileFormat(file.getFileType());
                        subtitles.setFileName(file.getFileName());
                        subtitles.setFilePath(file.getFileUrl());
                        subtitles.setFileSize(file.getFileSize());
                        System.out.println(track.getTrackTitle() + "字幕文件: " + subtitles);
                        subTitleList.add(subtitles);
                        break;
                    case OTHER:
                        System.out.println("其它文件: " + file.getFileName());
                        break;
                }
            });
        });
        trackRateService.saveBatch(trackRates);
        subtitlesService.saveBatch(subTitleList);
        return Boolean.TRUE;
    }

    @Override
    public List<String> tokenizeJapanese(String text) throws IOException {
        List<String> tokens = new ArrayList<>();
        JapaneseAnalyzer analyzer = new JapaneseAnalyzer();
        TokenStream tokenStream = analyzer.tokenStream("", new StringReader(text));
        CharTermAttribute termAttr = tokenStream.addAttribute(CharTermAttribute.class);
        tokenStream.reset();
        while (tokenStream.incrementToken()) {
            tokens.add(termAttr.toString());
        }
        tokenStream.end();
        tokenStream.close();
        analyzer.close();
        return tokens;
    }

    @Override
    public String cleanFileName(String fileName) {
        // 去掉后缀
        return fileName.replaceAll("\\.[^.]+$", "");
    }

    @Override
    public Map<CharSequence, Integer> getFreq(List<String> tokens) {
        Map<CharSequence, Integer> freq = new HashMap<>();
        for (String token : tokens) {
            freq.put(token, freq.getOrDefault(token, 0) + 1);
        }
        return freq;
    }

    @Override
    public Double calcCosine(List<String> tokens1, List<String> tokens2) {
        Map<CharSequence, Integer> freq1 = getFreq(tokens1);
        Map<CharSequence, Integer> freq2 = getFreq(tokens2);
        CosineSimilarity cosine = new CosineSimilarity();
        return cosine.cosineSimilarity(freq1, freq2);
    }

    // 聚合文件名
    @Override
    public Map<TrackInfo, List<FileRequest>> aggregateMultiRound(
            Map<TrackInfo, List<FileRequest>> groupMap,
            List<FileRequest> fileList,
            double initThreshold,
            double minThreshold,
            double step
    ) throws Exception {

        List<FileRequest> remaining = new ArrayList<>(fileList);
        double threshold = initThreshold;

        while (!remaining.isEmpty() && threshold >= minThreshold) {
            List<FileRequest> nextRemaining = new ArrayList<>();

            for (FileRequest file : remaining) {
                String coreName = cleanFileName(file.getFileName());
                TrackInfo bestMatchKey = null;
                double bestSim = 0;

                for (TrackInfo key : groupMap.keySet()) {
                    double sim = calcCosine(tokenizeJapanese(coreName), tokenizeJapanese(key.getTrackTitle()));
                    if (sim > bestSim) {
                        bestSim = sim;
                        bestMatchKey = key;
                    }
                }

                // 如果最佳相似度超过当前阈值，则匹配
                if (bestSim >= threshold && bestMatchKey != null) {
                    groupMap.get(bestMatchKey).add(file);
                } else {
                    nextRemaining.add(file); // 否则进入败者组，等待下轮匹配
                }
            }

            remaining = nextRemaining;
            threshold -= step;
        }

        // 剩下无法匹配的文件，单独处理
        for (FileRequest file : remaining) {
            System.out.println("未匹配文件: " + file.getFileName());
            // 创建“杂项对象”
        }

        return groupMap;
    }
}
